package com.mingqijia.gassafety.authority.dal.provider;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mingqijia.gassafety.authority.api.resp.company.CompanyLiteRespDTO;
import com.mingqijia.gassafety.authority.api.resp.department.DepartmentRespDTO;
import com.mingqijia.gassafety.authority.api.resp.manager.ManagerRespDTO;
import com.mingqijia.gassafety.authority.dal.command.*;
import com.mingqijia.gassafety.authority.dal.handler.CompanyTranslator;
import com.mingqijia.gassafety.authority.dal.handler.HierarchyTranslator;
import com.mingqijia.gassafety.authority.dal.handler.ManagerTranslator;
import com.mingqijia.gassafety.authority.dal.handler.RoleTranslator;
import com.mingqijia.gassafety.db.entity.auth.ManagerEntry;
import com.mingqijia.gassafety.db.mapper.auth.ManagerMapper;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.cqrs.QueryHandler;
import com.mingqijia.gassafety.shared.enums.ManagerType;
import com.mingqijia.gassafety.shared.enums.RunningState;
import com.mingqijia.gassafety.shared.enums.YesNoEnum;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import com.mingqijia.gassafety.shared.utils.MappingTools;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Consumer;

/**
 * 信息CU操作(C-create,U-update)
 * @author Damon S.
 * @version v0.2
 * @date 2021-06-19
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ManagerProvider extends ServiceImpl<ManagerMapper, ManagerEntry> implements QueryHandler<ManagerEntry> {

    private final ManagerTranslator translator;
    private final PasswordEncoder encoder;
    private final HierarchyProvider hProvider;
    private final RoleProvider rProvider;

    private final HierarchyTranslator hTranslator;
    private final RoleTranslator rTranslator;
    private final CompanyProvider cProvider;
    private final CompanyTranslator cTranslator;

    @Transactional(rollbackFor = Exception.class)
    public boolean changePassword(ChangePasswordCommand command) {
        if (Objects.equals(command.getOldPassword(), command.getNewPassword())) {
            throw new GlobalAuthenticationException("请设置不同的密码");
        }
        ManagerEntry entry = this.getByUsername(command.getUsername());
        if (ObjectUtils.isEmpty(entry)) {
            throw new GlobalAuthenticationException("账户不存在");
        }
        if (!encoder.matches(command.getOldPassword(), entry.getPassword())) {
            log.info("修改密码inputOld={},existOld={}", command.getOldPassword(), entry.getPassword());
            throw new GlobalAuthenticationException("当前密码不正确");
        }
        // 对新密码(已加盐)进行加密
        String encodedPassword = ApplicationUtils.encodePwd(command.getNewPassword());
        UpdateWrapper<ManagerEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().eq(ManagerEntry::getId, entry.getId())
                .set(ManagerEntry::getPassword, encodedPassword)
                .set(ManagerEntry::getForceChangePwd, YesNoEnum.N.getCode())
                .set(ManagerEntry::getUpdatedBy, ApplicationUtils.getUserId());

        return this.update(updateClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(String userId) {
        ManagerEntry entry = this.getById(userId);
        if (ObjectUtils.isEmpty(entry)) {
            throw new GlobalAuthenticationException("账户不存在");
        }

        String randomPwd = ApplicationUtils.lenLowerString(6);
        String encodedPassword = ApplicationUtils.encodePwd(
                randomPwd, entry.getUsername()
        );

        UpdateWrapper<ManagerEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().eq(ManagerEntry::getId, userId)
                .set(ManagerEntry::getForceChangePwd, YesNoEnum.Y.getCode())
                .set(ManagerEntry::getPassword, encodedPassword)
                .set(ManagerEntry::getUpdatedBy, ApplicationUtils.getUserId());

        return this.update(updateClause) ? randomPwd : null;
    }

    public ManagerEntry getByUsername(String username) {
        QueryWrapper<ManagerEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().and(qw -> qw.eq(ManagerEntry::getPhone, username)
                .or().eq(ManagerEntry::getUsername, username))
                .eq(ManagerEntry::getIsDeleted, YesNoEnum.N.getCode());
        return this.getOne(queryClause);
    }

    public List<ManagerRespDTO> buildManagerRel(List<ManagerEntry> entries) {
        List<ManagerRespDTO> managers = new ArrayList<>();
        entries.forEach(entry -> managers.add(buildManagerDTO(entry)));
        return managers;
    }

    public Page<ManagerEntry> find(QueryManagerCommand command) {
        LambdaQueryWrapper<ManagerEntry> queryClause = new LambdaQueryWrapper<>();
        Optional.ofNullable(command.getName()).ifPresent(
                name -> queryClause.like(ManagerEntry::getName, name)
        );
        Optional.ofNullable(command.getPhone()).ifPresent(
                phone -> queryClause.eq(ManagerEntry::getPhone, phone)
        );
        Optional.ofNullable(command.getUsername()).ifPresent(
                username -> queryClause.eq(ManagerEntry::getUsername, username)
        );
        if (Objects.nonNull(command.getState()) && !RunningState.NA.equals(command.getState())) {
            queryClause.eq(ManagerEntry::getState, command.getState().getCode());
        }
        if (!CollectionUtils.isEmpty(command.getRoleId())) {
            queryClause.and(buildRoleIdClause(command.getRoleId()));
        }
        if (StringUtils.hasText(command.getHierarchy())) {
            queryClause.like(ManagerEntry::getHierarchy, command.getHierarchy());
        }
        if (StringUtils.hasText(command.getDepartment())) {
            queryClause.like(ManagerEntry::getDepartment, command.getDepartment());
        }
        queryClause.eq(ManagerEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(ManagerEntry::getSpId, ApplicationUtils.getHeaderSpId())
                .orderByDesc(ManagerEntry::getCreatedAt);
        return this.page(createPage(command), queryClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public String create(CreateManagerCommand command) {
        this.attachOperator2Command(command);

        if (StringUtils.isEmpty(command.getHierarchy())) {
            command.setHierarchy(ApplicationUtils.getHeaderSpId());
        }

        QueryWrapper<ManagerEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(ManagerEntry::getIsDeleted, YesNoEnum.N.getCode())
                .and(qw -> qw.eq(ManagerEntry::getPhone, command.getPhone())
                .or().eq(ManagerEntry::getUsername, command.getUsername()));

        if (!Objects.equals(Constants.INT_ZERO, this.count(queryClause))) {
            throw new GlobalAuthenticationException("账户名或手机号已存在");
        }

        ManagerEntry entry = translator.toEntry(command);
        entry.setState(RunningState.STOPPED.getCode());
        entry.setPassword(ApplicationUtils.encodePwd(
                Constants.DEFAULT_PWD, command.getUsername()
        ));
        // 新建用户强制 登录更改密码
        entry.setForceChangePwd(YesNoEnum.Y.getCode());
        // 默认新建企业级用户（暂未使用）
        entry.setType(ManagerType.ENTERPRISE.getCode());

        if (!this.save(entry)) {
            throw new GlobalAuthenticationException("创建账号失败");
        }
        return entry.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean update(UpdateManagerCommand command) {
        this.attachOperator2Command(command);

        // 当更新的是平台管理员
        this.whenUpdate4SuperAdmin(command);

        List<String> userIds = Arrays.asList(
                command.getUserId().split(Constants.STR_COMMA)
        );
        UpdateWrapper<ManagerEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().in(ManagerEntry::getId, userIds);

        ManagerEntry entry = translator.toEntry(command);
        return this.update(entry, updateClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean remove(RemoveManagerCommand command) {
        this.attachOperator2Command(command);

        UpdateWrapper<ManagerEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().in(ManagerEntry::getId, command.getUserIds())
                .ne(ManagerEntry::getId, Constants.PLATFORM_SA)
                .set(ManagerEntry::getIsDeleted, YesNoEnum.Y.getCode())
                .set(ManagerEntry::getUpdatedBy, command.getUpdatedBy());

        return this.update(updateClause);
    }


    private ManagerRespDTO buildManagerDTO(ManagerEntry entry) {
        ManagerRespDTO resp = translator.toRespDTO(entry);
        if (StringUtils.hasText(entry.getRoleId())) {
            resp.setRoles(rTranslator.toRespDTO(
                    rProvider.listByIds(MappingTools.fromRaw(entry.getRoleId())))
            );
            resp.getRoles().forEach(role -> {
                role.setState(null); role.setDescription(null); role.setSpId(null);
            });
        }
        if (Constants.PLATFORM_SP.equalsIgnoreCase(ApplicationUtils.getHeaderSpId())
                && StringUtils.hasText(entry.getHierarchy())) {
            List<String> hierarchy = MappingTools.fromRaw(entry.getHierarchy());
            List<CompanyLiteRespDTO> liteResp =
                    cTranslator.toLiteRespDTO(cProvider.listByIds(hierarchy));

            // 按层级结构输出
            Map<String, CompanyLiteRespDTO> mapTemp = new HashMap<>(liteResp.size());
            liteResp.forEach(lite -> mapTemp.put(lite.getId(), lite));
            liteResp.clear();
            for (String item : hierarchy) {
                liteResp.add(mapTemp.get(item));
            }
            resp.setOrgs(liteResp);
        }
        if (!Constants.PLATFORM_SP.equalsIgnoreCase(ApplicationUtils.getHeaderSpId())
                && StringUtils.hasText(entry.getDepartment())) {
            List<String> departmentIds = MappingTools.fromRaw(entry.getDepartment());
            List<DepartmentRespDTO> departments =
                    hTranslator.toRespDTO(hProvider.listByIds(departmentIds));

            // 按层级结构输出
            Map<String, DepartmentRespDTO> entryMap = new HashMap<>(departments.size());
            if (!CollectionUtils.isEmpty(departments)) {
                departments.forEach(item -> entryMap.put(item.getId(), item));
            }
            departments.clear();
            // 解析部门
            DepartmentRespDTO depart;
            for (String departmentId : departmentIds) {
                depart = entryMap.get(departmentId);
                if (Objects.isNull(depart)) {
                    continue;
                }
                departments.add(depart);
            }
            resp.setDepartments(departments);
        }
        return resp;
    }

    @NotNull
    private Consumer<LambdaQueryWrapper<ManagerEntry>> buildRoleIdClause(List<String> roleIds) {
        return queryWhere -> {
            int length = roleIds.size();
            for (int index = Constants.INT_ZERO; index < length; index++) {
                if (index < length - Constants.INT_ONE) {
                    queryWhere.like(ManagerEntry::getRoleId, roleIds.get(index)).or();
                    continue;
                }
                queryWhere.like(ManagerEntry::getRoleId, roleIds.get(index));
            }
        };
    }

    private void whenUpdate4SuperAdmin(UpdateManagerCommand command) {
        if (Objects.equals(command.getUserId(), Constants.PLATFORM_SA) ||
                Objects.equals(command.getUsername(), Constants.PLATFORM_UN)) {
            if (StringUtils.hasText(command.getHierarchy()) &&
                    !command.getHierarchy().contains(Constants.PLATFORM_SP)) {
                command.setHierarchy(command.getHierarchy().concat(
                        Constants.STR_COMMA.concat(Constants.PLATFORM_SP)
                ));
            }
            if (StringUtils.hasText(command.getRoleId()) &&
                    !command.getRoleId().contains(Constants.PLATFORM_SR)) {
                command.setRoleId(command.getRoleId().concat(
                        Constants.STR_COMMA.concat(Constants.PLATFORM_SR)
                ));
            }
        }
    }
}
